/*
 * main_task_ops.c
 *
 *  Created on: 2024年8月19日
 *      Author: nwz
 */

#include <all_header.h>

#define MAX_APP_SIZE 			(APP_FLASH_SIZE-100)
#define MAX_BOOTLOADER_SIZE		(BOOTLOADER_FLASH_SIZE-100)

typedef struct {
	uint8_t identify[32];
	uint32_t crc;
	uint32_t size;
	uint8_t *data;
} application_header_t;

static const uint8_t app_identify[32] = { 0x77, 0x61, 0x74, 0x63, 0x68, 0x20,
		0x61, 0x70, 0x70, 0x20, 0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
		0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
		0x20, 0x20 };
static const uint8_t bootloader_identify[32] = { 0x77, 0x61, 0x74, 0x63, 0x68,
		0x20, 0x62, 0x6f, 0x6f, 0x74, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x72, 0x20,
		0x30, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x20, 0x20,
		0x20, 0x20, 0x20 };

static uint8_t buffer[CONSOLE_DMA_READ_BUFFER_SIZE / 2];

static void get_header_info(application_header_t *header) {
	memcpy(header, (void*) (flash_stream_task_get_start_addr()),
			sizeof(application_header_t) - 4);
	uint8_t *tmp = (uint8_t*) &header->size;
	header->size = (tmp[0] << 24) + (tmp[1] << 16) + (tmp[2] << 8) + tmp[3];
	tmp = (uint8_t*) &header->crc;
	header->crc = (tmp[0] << 24) + (tmp[1] << 16) + (tmp[2] << 8) + tmp[3];
	header->data = (uint8_t*) (flash_stream_task_get_start_addr() + 40);
}

static int check_identify(const uint8_t *d1, const uint8_t *d2) {
	for (int i = 0; i < 32; i++) {
		if (d1[i] != d2[i])
			return -1;
	}
	return 0;
}

static int check(uint8_t id, uint32_t max_size) {
	application_header_t head;
	get_header_info(&head);

	if (head.size > max_size) {
		return -1;
	}

	// check identity
	if (id == 1) { // app
		if (check_identify(head.identify, app_identify)) {
			return -2;
		}
	} else {
		if (check_identify(head.identify, bootloader_identify))
			return -2;
	}

	// crc
	uint32_t crc = calculate_crc32(head.data, head.size);
	if (crc != head.crc) {
		return -3;
	}

	return 0;
}

static void flash_write(uint8_t *data, uint32_t size,
		flash_stream_task_state_t state,
		void (*callback)(uint32_t size, uint32_t tsize)) {
	uint32_t write_size = 0;
	const uint32_t total_size = size;
	while (size > 0) {
		write_size = min(size, flash_stream_task_get_avaliabe_size());
		if (write_size > 0) {
			ASSERT(
					flash_stream_task_write_buffer(data, write_size, state)
							== write_size);
			if (callback)
				callback(write_size, total_size);
			data += write_size;
			size -= write_size;
		} else {
			flash_stream_task_wait(portMAX_DELAY);
		}
	}
}

int main_task_ops_download(void (*callback)(uint32_t size, uint32_t file_size)) {
	int ret = 0;
	int tsize = 0;
	int size;

	//
	flash_stream_task_set_state(Flash_Stream_Task_State_Open);
	flash_stream_task_set_state(Flash_Stream_Task_State_Erase_Download);
	callback(0, 0);

	console_read_task_clear();
	size = console_read_task_read(buffer, sizeof(buffer), portMAX_DELAY);
	ASSERT(size > 0);
	tsize += size;
	while (true) {
		// write flash
		flash_write(buffer, size, Flash_Stream_Task_State_Write_Download, NULL);
		// read data
		size = console_read_task_read(buffer, sizeof(buffer), 1000);
		// check size
		tsize += size;

		callback(tsize, 0);
		if (size == 0) {
			if (tsize < 10 * 1024) {
				ret = -2;
			} else {
				ret = tsize;
			}
			break;
		} else if (tsize > MAX_APP_SIZE) {
			ret = -1;
			break;
		}
	}

	flash_stream_task_set_state(Flash_Stream_Task_State_Close);
	return ret;
}

int main_task_ops_check_app(void) {
	return check(1, MAX_APP_SIZE);
}

int main_task_ops_check_bootloader(void) {
	return check(0, MAX_BOOTLOADER_SIZE);
}

static int check_copy(uint32_t start) {
	application_header_t head;
	get_header_info(&head);

	// crc
	uint32_t crc = calculate_crc32((uint8_t*) (start), head.size);
	if (crc != head.crc) {
		return -1;
	}
	return 0;
}

int main_task_ops_check_app_copy(void) {
	return check_copy(FLASH_MAP_ADDR + APP_FLASH_START);
}

int main_task_ops_check_bootloader_copy(void) {
	return check_copy(FLASH_MAP_ADDR + BOOTLOADER_FLASH_START);
}

int main_task_ops_copy_app(void (*callback)(uint32_t size, uint32_t tsize)) {
	application_header_t head;
	get_header_info(&head);

	flash_stream_task_set_state(Flash_Stream_Task_State_Open);
	flash_stream_task_set_state(Flash_Stream_Task_State_Erase_App);
	flash_write(head.data, head.size, Flash_Stream_Task_State_Write_App,
			callback);
	flash_stream_task_set_state(Flash_Stream_Task_State_Close);

	flash_stream_task_wait_until_finish();
	return 0;
}

int main_task_ops_copy_bootloader(
		void (*callback)(uint32_t size, uint32_t tsize)) {
	application_header_t head;
	get_header_info(&head);

	flash_stream_task_set_state(Flash_Stream_Task_State_Open);
	flash_stream_task_set_state(Flash_Stream_Task_State_Erase_BootLoader);
	flash_write(head.data, head.size, Flash_Stream_Task_State_Write_BootLoader,
			callback);
	flash_stream_task_set_state(Flash_Stream_Task_State_Close);

	flash_stream_task_wait_until_finish();
	return 0;
}
